package webserver;

import java.util.Collection;
import java.util.HashMap;

// <editor-fold defaultstate="collapsed" desc=" UML Marker "> 
// #[regen=yes,id=DCE.9C037D9D-F05B-6B8D-561B-424BD60D5353]
// </editor-fold>
/**
 * Cache class represents a simple Caching model
 * Requested files will be cached and directly served when they are in cache
 * 
 */
public class Cache {

	// <editor-fold defaultstate="collapsed" desc=" UML Marker ">
	// #[regen=yes,id=DCE.9F561965-91D3-5D33-17BF-08BD22631F6F]
	// </editor-fold>
	// Static Declarations
	private static HashMap<String, CacheItem> cache;
	private static Cache instance = null;

	// <editor-fold defaultstate="collapsed" desc=" UML Marker ">
	// #[regen=yes,id=DCE.40558090-44F0-6E1D-411E-C1C1CBEDB131]
	// </editor-fold>
	/**
	 * Cache Default Constructor
	 * Private for Singleton-DesignPattern
	 */
	private Cache() {
		cache = new HashMap<String, CacheItem>();
	}

	// <editor-fold defaultstate="collapsed" desc=" UML Marker ">
	// #[regen=yes,id=DCE.326A2338-EB83-3900-405F-2402DBF46B79]
	// </editor-fold>
	/**
	 * Get the current Cache instance
	 * @return Cache - the static Cache
	 */
	public static Cache getInstance() {
		if (instance == null) {
			instance = new Cache();
		}
		return instance;
	}

	// <editor-fold defaultstate="collapsed" desc=" UML Marker ">
	// #[regen=yes,id=DCE.77AE5D87-4F1E-EA41-3A7F-52B64FAD5F4A]
	// </editor-fold>
	/**
	 * Get a files contents with relative path
	 * @param path - The relative Path to the file
	 * @param forceRecache - force the recaching of the file
	 * @return byte[] - File contents (can be null or length = 0)
	 */
	public synchronized byte[] getFileContents(String path, boolean forceRecache) {
		String absPath = AppProperties.getInstance().getHomeDirectory() + "/" + path;

		// Check if the item is cached or cache it
        /*File newItem = new File(absPath);
		if (!newItem.exists()) {
		return null;
		}*/

		// Cache needed file
		return getFileContentsAbsPath(absPath, forceRecache);
	}

	/**
	 * Get a files contents with absolute path
	 * @param absPath - The absolute path to the file
	 * @param forceRecache - force the recaching of the file
	 * @return byte[] - File contents (can be null or length = 0)
	 */
	public synchronized byte[] getFileContentsAbsPath(String absPath, boolean forceRecache) {
		absPath = absPath.replace('/', '\\');

		// Check if item MUST be recached
		if (forceRecache && cache.containsKey(absPath)) {
			cache.remove(absPath);
		}

		// Check if item is already cached
		if (!cache.containsKey(absPath)) {
			cache.put(absPath, new CacheItem(absPath));
		}

		return cache.get(absPath).getData();
	}

	/**
	 * Get the corresponding CacheItem to a Path
	 * @param path - Path of the wanted resource
	 * @return CacheItem (can be null)
	 */
	public synchronized CacheItem getCacheItem(String path) {
		String temp = path.replace('/', '\\');
		String absPath = AppProperties.getInstance().getHomeDirectory() + "\\" + temp;

		// Cache needed file
		if (!cache.containsKey(absPath)) {
			return null;
		}

		return cache.get(absPath);
	}

	/**
	 * Will check if the given Path is cached
	 * @param path
	 * @return boolean
	 */
	public synchronized boolean isCached(String path) {
		//String absPath = AppProperties.getInstance().getHomeDirectory() + path;
		return cache.containsKey(path);
	}

	/**
	 * Add a file to the Cache
	 * @param path
	 */
	public synchronized void cache(String path) {
		// Check if the item is cached or cache it
		String temp = path.replace('/', '\\');
		if (!cache.containsKey(temp)) {
			cache.put(temp, new CacheItem(temp));
		}
	}

	/**
	 * Get the contents (byte[]) of a stored ErrorPage if available
	 * @param statusCode - the corresponsing errorpage will be searched
	 * @return errorpage as byte array
	 */
	public synchronized byte[] getErrorPage(int statusCode) {

		byte[] temp = this.getFileContentsAbsPath(AppProperties.getInstance().getErrorDirectory() + "\\" + statusCode + ".htm", false);
		if (temp == null || temp.length == 0) {
			return null;
		} else {
			return temp;
		}
	}

	/**
	 * The function will check the whole cache for invalid entries
	 */
	public synchronized void checkForInvalidEntries() {
		Collection<CacheItem> items = cache.values();
		for (CacheItem item : items) {
			if (this.isCached(item.getAbsPath()) && !item.isValid()) {
				items.remove(item);
			}
		}
	}
}

